// LocalStorage polyfill
(function(c){if(!c){var d={},undef;window.localStorage={setItem:function(a,b){return d[a]=String(b)},getItem:function(a){return d.hasOwnProperty(a)?d[a]:undef},removeItem:function(a){return delete d[a]},clear:function(){return d={}}}}})((function(){try{return"localStorage"in window&&window.localStorage!=null}catch(e){return false}})());


// Make it safe to do console.__functionName__() always
(function (con) {
  var method, dummy = function() {}, methods = ('assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn, memory').split(',');
  while (method = methods.pop()) { con[method] = con[method] || dummy; }
})(window.console = window.console || {});

// jQuery crossdomain request fix
// https://github.com/jaubourg/ajaxHooks/blob/master/src/xdr.js
if(window.XDomainRequest){jQuery.ajaxTransport(function(b){if(b.crossDomain&&b.async){if(b.timeout){b.xdrTimeout=b.timeout;delete b.timeout}var a;return{send:function(d,c){function e(f,i,h,g){a.onload=a.onerror=a.ontimeout=jQuery.noop;a=undefined;c(f,i,h,g)}a=new XDomainRequest();a.onload=function(){e(200,"OK",{text:a.responseText},"Content-Type: "+a.contentType)};a.onerror=function(){e(404,"Not Found")};a.onprogress=jQuery.noop;a.ontimeout=function(){e(0,"timeout")};a.timeout=b.xdrTimeout||Number.MAX_VALUE;a.open(b.type,b.url);a.send((b.hasContent&&b.data)||null)},abort:function(){if(a){a.onerror=jQuery.noop;a.abort()}}}}})};

var LT = LT || {};

// App config
LT.config = {
    balanceDisabled: false
};

LT.current = {
    user: {}
};

(function ($, LT) {
    'use strict';

    // Geo detection
    LT.GeoDictionary = (new function () {
        var cache = JSON.parse(localStorage.getItem('GeoDictionary'));
        if (!cache) {
            cache = {};
        }

        this.getCacheKey = function (params) {
            var p = [];
            p.push(params.name || 'all');
            if (params.parent) {
                p.push(params.parent);
            }
            return p.join('.');
        };

        this.getListCities = function (params) {
            var _params = {
                    name: undefined,
                    parent: undefined,
                    emptyText: '...',
                    success: function () {},
                    complete: function () {},
                    error: function () {}
                },
                cacheKey;
            params && $.extend(_params, params);
            cacheKey = this.getCacheKey(_params);

            if (cacheKey in cache
                && _.isObject(cache[cacheKey])
                && cache[cacheKey].version == (LT.version + LT.lang)) {
                    $.isFunction(_params.success) && _params.success(cache[cacheKey].data);
                    $.isFunction(_params.complete) && _params.complete();
                    return true;
            }

            $.request({
                type: 'POST',
                url: '/users/getRelatedList',
                data: {
                    name: _params.name,
                    parent_id: _params.parent
                },
                success: function (res) {
                    if (res && $.isArray(res.data)) {
                        if (res.data[0] && !res.data[0].value) {
                            res.data[0].text = _params.emptyText;
                        }
                        else {
                            res.data.unshift({
                                value: '',
                                text: '&mdash;'
                            });
                        }
                        cache[cacheKey] = {
                            data: res.data,
                            version: (LT.version + LT.lang)
                        };
                        localStorage.setItem('GeoDictionary', JSON.stringify(cache));
                        $.isFunction(_params.success) && _params.success(res.data);
                    }
                    else {
                        $.isFunction(_params.error) && _params.error(res);
                    }
                },
                error: _params.error,
                complete: _params.complete
            });
        };
    });

    // Check Types
    $.extend(LT, {
        is_function: function (func) {
            return (func && typeof(func) ==='function');
        },
        is_object: function (o) {
            return (typeof(o) === 'object');
        },
        is_array: function (o) {
            return (o !== null && typeof(o) === 'object' && o.unshift !== undefined);
        },
        is_string: function (o) {
        return (typeof(o) === 'string');
        },
        is_numeric: function (o){
            return !isNaN(o);
        },
        is_null: function (o){
            return o === null;
        },
        isEmail: function (str) {
            var pt=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i;
            return str?pt.test(str):false;
        },
        is_date: function (o) {
            return (o && typeof(o) == 'object' && LT.is_function(o.getTime));
        },
        is_valid_date: function (o) {
            return (LT.is_date(o) && o.getDay && !isNaN(o.getDay()));
        },
        method_exists: function (obj, m) {
            return (obj && LT.is_object(obj) && LT.is_function((obj)[m]) );
        }
    });

    /**
     * Save cache into localStorage
     */
    LT.saveStorage = function (storageKey, obj) {
        localStorage.setItem(storageKey, JSON.stringify(obj));
    };

    /**
     * Load cache from localStorage
     */
    LT.loadStorage = function (storageKey) {
        return JSON.parse(localStorage.getItem(storageKey)) || {};
    };

    /**
     * Remove cache from localStorage
     */
    LT.removeStorage = function (storageKey) {
        localStorage.removeItem(storageKey);
    };

    /**
     * Global for translations
     * @type {{}}
     */
    LT.text = {};

    LT.t = function (key, val) {
        if ($.type(key) == 'object') $.each(key, this.t.bind(this));
        else if (val) {
            if (this.text[key]) {
                console.warn('LT.text["' + key + '"] exists and is "' + this.text[key] + '"! Replacing with "' + val + '".');
            }
            this.text[key] = val;
        }
        else if (this.text[key]) {
            return this.text[key];
        }
        else {
            console.warn('LT.text["' + key + '"] does not exists! Returning key passed: "' + key + '".');
            return key;
        }
    };

    /**
     * Global helpers
     * @type {object}
     */
    LT.Helpers = {};


    /* Template helpers
     * ========================================= */
    LT.Helpers.Template = function () {
        var _options = {
                // path of templates
                templatesUrl: null,

                // max connections if first is fail (not 404 error)
                maxTryConnections: 2
            },

            // saver of count try connections
            countTryConnections = 0;

        /**
         * Templates
         * @type {jQuery}
         */
        this.$templates = $();

        /**
         * Cached of comiled templates
         * @type {object}
         */
        this.compiledTemplates = {};

        /**
         * Fisrt init. Get templates.
         * @param {object} options
         * @param callback
         */
        this.init = function (options, callback) {
            _.extend(_options, options);
            if (!this.$templates.length) {
                this._getTemplates(callback);
            }
            else {
                $.isFunction(callback) && callback(this.$templates);
            }
        };

        /**
         * Get template by name
         * @param {string} tmplName
         * @returns {function}
         */
        this.getTemplate = function (tmplName) {
            var html = this.$templates.filter('[template=' + tmplName + ']').html();
            return this.compileTemplate(tmplName, html);
        };

        /**
         * Compile template
         * @param {string} tmplName
         * @param {string} html
         * @returns {function}
         */
        this.compileTemplate = function (tmplName, html) {
            if (tmplName && html) {
                this.compiledTemplates[tmplName] = _.template(html);
                return this.compiledTemplates[tmplName];
            }
            else {
                console.warn('compileTemplate: Template name and html should not be empty!');
                return _.template(' >>>Compile template error<<< ');
            }
        };

        /**
         * Generate template
         * @param {string} tmplName
         * @param {object} data
         */
        this.generate = function (tmplName, data) {
            if (!this.compiledTemplates[tmplName]) {
                return this.getTemplate(tmplName)(data);
            }
            else {
                return this.compiledTemplates[tmplName](data);
            }
        };


        /**
         * Get templates by ajax
         * @param callback
         * @private
         */
        this._getTemplates = function (callback) {
            if (_options.templatesUrl) {
                $.ajax({
                    type: 'GET',
                    url: _options.templatesUrl
                })
                .complete(function (res) {
                    switch (res.status) {
                        case 200:
                            this.$templates = $(res.responseText);
                            $.isFunction(callback) && callback(this.$templates);
                            break;
                        case 404:
                            console.warn('Check path. 404 Cann\'t found ', _options.templatesUrl);
                            break;
                        default:
                            if (countTryConnections < _options.maxTryConnections) {
                                countTryConnections++;
                                this._getTemplates(callback);
                            }
                            else {
                                console.warn('Something\'s wrong, cann\'t get template: ', _options.templatesUrl);
                                countTryConnections = 0;
                            }
                            break;
                    };
                }.bind(this));
            }
            else {
                console.warn('First you must set options!');
            }
        };
    };


    /**
     * Timer helper for debugging
     */
    LT.Helpers.Timer = (function () {
        var timerStart = false,
            defaultName = 'Timer';

        /**
         * Start/end timer
         * @param {string} timerName
         */
        return function(timerName) {
            timerName = timerName || defaultName;
            if (!timerStart) {
                timerStart = timerName;
                console.time(timerName);
            }
            else {
                console.timeEnd(timerStart);
                timerStart = false;
            }
        };
    }());


    /* Text helpers
     * ========================================= */
    LT.Helpers.Text = {};

    LT.Helpers.Text.plural = function () {
        var
            num = arguments[0],
            endings = Array.prototype.slice.call(arguments, 1, 4),
            replace = !!arguments[4],
            r10, r100, plr;

        if (_.isNaN(num)) {
            return;
        }

        if (!endings.length) {
            return num;
        }

        r10 = num % 10;
        r100 = num % 100;
        plr = (r10 == 1 && r100 != 11)
            ? 0
            : ((r10 >= 2 && r10 <= 4 && (r100 < 10 || r100 >= 20) ) ? 1 : 2);
        return replace
            ? endings[plr].replace('%1', num)
            : num + ' ' + endings[plr];
    };

    /**
     * Remove tags from text
     * @param {string} str
     * @param {string|Array} inc
     * @returns {string}
     */
    LT.Helpers.Text.stripTags = function (str, inc) {
        if (!str) {
            return '';
        }
        var pt = '<.*?>';
        if (inc) {
            pt = '<\\/?\s*(?:' + ( $.isArray(inc) ? inc.join('|') : inc.toString() ) + ')(\\s[^>]+)*\\/?>';
        }
        return str.toString().replace(new RegExp(pt, 'g'), '');
    };

    LT.Helpers.Text.br2nl = function (str) {
        return str ? str.toString().replace( new RegExp('<br\s*\/?>', 'ig') , "\r\n" ) : '';
    };

    LT.Helpers.Text.nl2br = function (str) {
        return str ? str.replace( /\r?\n/g , '<br/>' ) : '';
    };

    LT.Helpers.Text.text2json = function (text) {
        return JSON.parse(LT.Helpers.Text.stripTags(text));
    };


    /* Date helpers
     * ========================================= */
    LT.Helpers.Date = {};

    LT.Helpers.Date.currentDate = function () {
        var dt = new Date(),
            time = dt.getTime();

        if (FLRT.timestampDiff) {
            time = time + (FLRT.timestampDiff * 1000);
            return new Date(time);
        }

        return dt;
    };

    /**
     * Functions to return date difference in integer number
     * @type {{days: Function, weeks: Function, months: Function, years: Function}}
     */
    LT.Helpers.Date.diff = {

        /**
         * Return difference of days
         * @param {Date} d1
         * @param {Date} d2
         * @returns {Number}
         */
        days: function (d1, d2) {
            var t2 = d2.getTime();
            var t1 = d1.getTime();

            return parseInt((t2 - t1) / (24 * 3600 * 1000));
        },

        /**
         * Return difference of weeks
         * @param {Date} d1
         * @param {Date} d2
         * @returns {Number}
         */
        weeks: function (d1, d2) {
            var t2 = d2.getTime();
            var t1 = d1.getTime();

            return parseInt((t2 - t1) / (24 * 3600 * 1000 * 7));
        },

        /**
         * Return difference of months
         * @param {Date} d1
         * @param {Date} d2
         * @returns {Number}
         */
        months: function (d1, d2) {
            var d1Y = d1.getFullYear();
            var d2Y = d2.getFullYear();
            var d1M = d1.getMonth();
            var d2M = d2.getMonth();

            return (d2M + 12 * d2Y) - (d1M + 12 * d1Y);
        },

        /**
         * Return difference of years
         * @param {Date} d1
         * @param {Date} d2
         * @returns {Number}
         */
        years: function (d1, d2) {
            return d2.getFullYear() - d1.getFullYear();
        }

    };

    /**
     * Functions to return date difference in string format (localization)
     * @type {{days: Function}}
     */
    LT.Helpers.Date.diffString = {

        /**
         * Return difference of days
         * @param {Date} d1
         * @param {Date} d2
         * @returns {String}
         */
        days: function (d1, d2) {
            var diff = LT.Helpers.Date.diff.days(d1, d2);
            switch (diff) {
                case 0:
                    return 'today';
                case 1:
                    return 'tomorrow';
                case 2:
                    return 'after_tomorrow';
                case -1:
                    return LT.text.dateYesterday;
                case -2:
                    return LT.text.date2DaysAgo;
                default:
                    if (diff > 0) {
                        return 'core:after_days';
                    }
                    else {
                        return Math.abs(diff) + ' ' + LT.text.dateDaysBefore;
                    }
            }
        }
    };

    /**
     * Return human date
     * @param ts
     * @returns {String}
     * @constructor
     */
    LT.Helpers.Date.Human = function (ts) {
        var from = new Date(ts),
            to = LT.Helpers.Date.currentDate(),

            diff = to - from,

            // the distance (in seconds) with decimals
            distance = (diff > 0 ? diff : 0) / 1000,

            // rounding off the distance in minutes and in seconds
            distance_in_minutes = Math.round(distance / 60),
            distance_in_hours = Math.round(distance_in_minutes / 60),

            diffDays, diffYears;

        if (distance < 10) {
            return LT.text.dateMomentAgo;
        }
        else if (distance < 60) {
            return LT.Helpers.Text.plural(
                Math.round(distance / 5) * 5,
                LT.text.dateSecondAgo_plural_1,
                LT.text.dateSecondAgo_plural_2,
                LT.text.dateSecondAgo_plural_3
            )
        }
        else if (distance < 90) {
            return LT.text.dateMinuteAgo_plural_1;
        }
        else if (distance < 150) {
            return LT.text.dateCoupleMinutesAgo;
        }
        else if (distance_in_minutes < 45) {
            return LT.Helpers.Text.plural(distance_in_minutes,
                LT.text.dateMinuteAgo_plural_1,
                LT.text.dateMinuteAgo_plural_2,
                LT.text.dateMinuteAgo_plural_3
            );
        }
        else if (distance_in_minutes < 90) {
            return LT.text.dateHourAgo_plural_1;
        }
        else if (distance_in_hours < 24) {
            return LT.Helpers.Text.plural(distance_in_hours,
                LT.text.dateHourAgo_plural_1,
                LT.text.dateHourAgo_plural_2,
                LT.text.dateHourAgo_plural_3
            );
        }
        else if (distance_in_hours < 48) {
            return LT.text.dateYesterday + ', ' + from.dateFormat('H:m');
            return LT.Helpers.Text.plural(distance_in_hours,
                LT.text.dateHourAgo_plural_1,
                LT.text.dateHourAgo_plural_2,
                LT.text.dateHourAgo_plural_3
            );
        }
        else {
            diffDays = LT.Helpers.Date.diff.days(from, to);
            diffYears = LT.Helpers.Date.diff.years(from, to);
            if (diffDays < 1) {
                return LT.Helpers.Text.plural(distance_in_hours,
                    LT.text.dateHourAgo_plural_1,
                    LT.text.dateHourAgo_plural_2,
                    LT.text.dateHourAgo_plural_3
                );
            }
            else if (diffDays < 3) {
                return LT.Helpers.Date.diffString.days(to, from) + from.dateFormat(', H:i');
            }
            else if (diffYears < 1) {
                return from.dateFormat('j F, H:i');
            }
            return from.dateFormat('j F Y, H:i');
        }
    };
}(window.jQuery, LT));

/**
 * Simulates PHP's date formating function
 * @param {String} format
 * @returns {String}
 */
Date.prototype.dateFormat = function (format) {
    if (!_.isUndefined(FLRT.time_offset)) {
        var offset = ((+ FLRT.time_offset) + (new Date()).getTimezoneOffset()*60) * 1000;
        this.setTime(this.getTime() + offset);
    }
    var returnStr = '';
    var replace = Date.replaceChars;
    for (var i = 0; i < format.length; i++) {
        var curChar = format.charAt(i);
        if (i - 1 >= 0 && format.charAt(i - 1) == "\\") {
            returnStr += curChar;
        }
        else if (replace[curChar]) {
            returnStr += replace[curChar].call(this);
        } else if (curChar != "\\") {
            returnStr += curChar;
        }
    }
    return returnStr;
};

Date.replaceChars = {
    shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
    shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],

    // Day
    d: function () {
        return (this.getDate() < 10 ? '0' : '') + this.getDate();
    },
    D: function () {
        return Date.replaceChars.shortDays[this.getDay()];
    },
    j: function () {
        return this.getDate();
    },
    l: function () {
        return Date.replaceChars.longDays[this.getDay()];
    },
    N: function () {
        return this.getDay() + 1;
    },
    S: function () {
        return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this.getDate() % 10 == 2 && this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 3 && this.getDate() != 13 ? 'rd' : 'th')));
    },
    w: function () {
        return this.getDay();
    },
    z: function () {
        var d = new Date(this.getFullYear(), 0, 1);
        return Math.ceil((this - d) / 86400000);
    }, // Fixed now
    // Week
    W: function () {
        var d = new Date(this.getFullYear(), 0, 1);
        return Math.ceil((((this - d) / 86400000) + d.getDay() + 1) / 7);
    }, // Fixed now
    // Month
    F: function () {
        return Date.replaceChars.longMonths[this.getMonth()];
    },
    m: function () {
        return (this.getMonth() < 9 ? '0' : '') + (this.getMonth() + 1);
    },
    M: function () {
        return Date.replaceChars.shortMonths[this.getMonth()];
    },
    n: function () {
        return this.getMonth() + 1;
    },
    t: function () {
        var d = new Date();
        return new Date(d.getFullYear(), d.getMonth(), 0).getDate()
    }, // Fixed now, gets #days of date

    // Year
    L: function () {
        var year = this.getFullYear();
        return (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0));
    },   // Fixed now
    o: function () {
        var d = new Date(this.valueOf());
        d.setDate(d.getDate() - ((this.getDay() + 6) % 7) + 3);
        return d.getFullYear();
    }, //Fixed now
    Y: function () {
        return this.getFullYear();
    },
    y: function () {
        return ('' + this.getFullYear()).substr(2);
    },

    // Time
    a: function () {
        return this.getHours() < 12 ? 'am' : 'pm';
    },
    A: function () {
        return this.getHours() < 12 ? 'AM' : 'PM';
    },
    B: function () {
        return Math.floor((((this.getUTCHours() + 1) % 24) + this.getUTCMinutes() / 60 + this.getUTCSeconds() / 3600) * 1000 / 24);
    }, // Fixed now
    g: function () {
        return this.getHours() % 12 || 12;
    },
    G: function () {
        return this.getHours();
    },
    h: function () {
        return ((this.getHours() % 12 || 12) < 10 ? '0' : '') + (this.getHours() % 12 || 12);
    },
    H: function () {
        return (this.getHours() < 10 ? '0' : '') + this.getHours();
    },
    i: function () {
        return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes();
    },
    s: function () {
        return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds();
    },
    u: function () {
        var m = this.getMilliseconds();
        return (m < 10 ? '00' : (m < 100 ?
            '0' : '')) + m;
    },

    // Timezone
    e: function () {
        return "Not Yet Supported";
    },
    I: function () {
        var DST = null;
        for (var i = 0; i < 12; ++i) {
            var d = new Date(this.getFullYear(), i, 1);
            var offset = d.getTimezoneOffset();

            if (DST === null) DST = offset;
            else if (offset < DST) {
                DST = offset;
                break;
            } else if (offset > DST) break;
        }
        return (this.getTimezoneOffset() == DST) | 0;
    },
    O: function () {
        return (-this.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(this.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(this.getTimezoneOffset() / 60)) + '00';
    },
    P: function () {
        return (-this.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(this.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(this.getTimezoneOffset() / 60)) + ':00';
    }, // Fixed now
    T: function () {
        var m = this.getMonth();
        this.setMonth(0);
        var result = this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, '$1');
        this.setMonth(m);
        return result;
    },
    Z: function () {
        return -this.getTimezoneOffset() * 60;
    },
    // Full Date/Time
    c: function () {
        return this.dateFormat("Y-m-d\\TH:i:sP");
    }, // Fixed now
    r: function () {
        return this.toString();
    },
    U: function () {
        return this.getTime() / 1000;
    }
};
